package coderead.agent.jvmtool;
/**
 * 远程服务类
 */

import coderead.agent.HelloWorld;
import com.sun.jdi.VirtualMachine;
import javassist.*;

import javax.sound.midi.Soundbank;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.lang.instrument.UnmodifiableClassException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.RemoteException;

import java.rmi.server.UnicastRemoteObject;
import java.security.ProtectionDomain;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;


import static java.util.Arrays.*;


public class ToolServiceImpl extends UnicastRemoteObject implements ToolService {
    public ToolServiceImpl() throws RemoteException {

    }



    @Override
    public String findClassName(String name) throws RemoteException {
        return stream(Agent.instrumentation.getAllLoadedClasses())
                .filter(s -> s.getName().toUpperCase().contains(name.toUpperCase()))
                .limit(20)
                .map(Class::getName)
                .collect(Collectors.joining("\r\n"));
    }

    @Override
    public String jadClass(String className) throws RemoteException {
        try {
            return Jad.decompiler(className);
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
    }

    @Override
    public String getClassAllMethods(String className , String arg) throws RemoteException {
        return stream(stream(Agent.instrumentation.getAllLoadedClasses())
                .filter(s -> s.getName().toUpperCase().equals(className.toUpperCase()))
                .findFirst()
                .get().getMethods()).map(Method::getName)
                .collect(Collectors.joining("\r\n"));


    }


    @Override
    public String modfiymethod(String className , String methodname , String[] befor , String[] after) throws RemoteException, NotFoundException, CannotCompileException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InstantiationException, InvocationTargetException {
        String replace = className.replace(".", "/");
        String targetclassname = className;
        Agent.instrumentation.addTransformer(new ClassFileTransformer() {
            @Override
            public byte[] transform(
                    ClassLoader loader,
                    String className, Class<?> classBeingRedefined,
                    ProtectionDomain protectionDomain,
                    byte[] classfileBuffer) throws IllegalClassFormatException {
                //System.out.println(replace+"----"+methodname);
                if (!replace.equals(className)) {
                   // System.out.println(targetclassname+"---"+className);
                    return null;
                }
                try {
                    ClassPool pool=new ClassPool();
                   // System.out.println("pool");


                    pool.appendSystemPath();
                    CtClass ctClass = pool.get(targetclassname);
                    CtMethod ctMethod = ctClass.getDeclaredMethod(methodname);
                    //插入前置逻辑
                    for (int i = 0; i < befor.length; i++) {
                        ctMethod.insertBefore(befor[i]);
                    }
                    for (int i = 0; i < after.length; i++) {
                        ctMethod.insertAfter(after[i]);
                    }
                    return ctClass.toBytecode();
                } catch (NotFoundException | CannotCompileException | IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        },true);
        // 重新走过滤器
        try {
            Agent.instrumentation.retransformClasses(Class.forName(targetclassname));
        } catch (UnmodifiableClassException e) {
            e.printStackTrace();
        }
        Method method = Class.forName(className).getMethod(methodname);
        method.invoke(Class.forName(className).newInstance());
        return "success";
    }

    @Override
    public String recovermethod(String targetclassName, String methodname) throws RemoteException, ClassNotFoundException, IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException {
        Agent.instrumentation.addTransformer(new ClassFileTransformer() {
            @Override
            public byte[] transform(
                    ClassLoader loader,
                    String className, Class<?> classBeingRedefined,
                    ProtectionDomain protectionDomain,
                    byte[] classfileBuffer) throws IllegalClassFormatException {
                if (!targetclassName.replace(".","/").equals(className)) {

                    return null;
                }
                try {
                    ClassPool pool=new ClassPool();
                    pool.appendSystemPath();
                    CtClass ctClass = pool.get(targetclassName);
                    return ctClass.toBytecode();
                } catch (NotFoundException | CannotCompileException | IOException e) {
                    e.printStackTrace();
                }
                return null;
            }
        },true);
        // 重新走过滤器
        try {
            Agent.instrumentation.retransformClasses(Class.forName(targetclassName));
        } catch (UnmodifiableClassException e) {
            e.printStackTrace();
        }
        Method method = Class.forName(targetclassName).getMethod(methodname);
        method.invoke(Class.forName(targetclassName).newInstance());
        return "success";
    }

    @Override
    public Long getClasssize(String classname) throws RemoteException, IllegalAccessException, InstantiationException {

        Object o = stream(Agent.instrumentation.getAllLoadedClasses())
                .filter(s -> s.getName().equals(classname))
                .findFirst()
                .get()
                .newInstance();

        long objectSize = Agent.instrumentation.getObjectSize(o);

        return objectSize ;
    }

    @Override
    public String getCurrliveObject(String classname) throws ClassNotFoundException, RemoteException {
        Object[] instances = Agent.vmTool.getInstances(Class.forName(classname));
        String result = Arrays.stream(instances)
                .map(Object::toString)
                .collect(Collectors.joining("\r\n"));
        return result;
    }

    @Override
    public String getClassAttributes(String classname) throws ClassNotFoundException, RemoteException, IllegalAccessException {
        String[] Name = classname.split("@") ;
        if (Name.length!=2) {
            return "请输入实例名 ClassName@xxxxx , 请先使用getinstance 获取实例名";
        }
        StringBuffer buffer = new StringBuffer();
        Object[] instances = Agent.vmTool.getInstances(Class.forName(Name[0]));
        Object o = stream(instances)
                .filter(s -> s.toString().equals(classname))
                .findFirst()
                .get();
        //Map<String , Object> attributes = new HashMap<>();
        Field[] fields = o.getClass().getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            fields[i].setAccessible(true);
            buffer.append(fields[i].toGenericString()+"---"+ fields[i].get(instances[i])+"\r\n");
        }
       return buffer.toString();
    }
}
